In C++, every independent object needs to have a unique address, which implies that its size cannot be null. Sub-objects of another object,
however, do not have this constraint. Empty base class subobjects usually don’t take any space in the final object, but empty member variables, by
default, take at least one byte. The impact on the object’s size may be even larger due to padding and alignment requirements.
C++20 introduces the [[no_unique_address]]
attribute. It indicates that preserving the uniqueness of the address guarantee is not
important for the decorated member variable. If the variable type is empty, no storage needs to be reserved for it in the class.
If the type is not empty, this attribute is still valid and has no effect. This allows placing this attribute on dependent member variables in
template classes and having the exact behavior depend on the template parameters.
This rule raises an issue on each member of a class that has an empty or potentially empty (in case of templates) type and does not have a
[[no_unique_address]]
attribute.
Note: This rule is disabled on Windows because [[no_unique_address]]
isn’t well supported by MSVC and Clang on
this platform.
Noncompliant code example
struct Empty {};
struct Wrapped {
int* ptr;
Empty e; // Noncompliant
}; // sizeof(Wrapped) is > sizeof(int*)
template<typename K, typename V, typename Hash, typename Equal>
class HashMap {
/* ... */
Hash hash; // Noncompliant if HashMap is instantiated with an empty Hash
Equal equal; // Noncompliant if HashMap is instantiated with an empty Equal
};
Compliant solution
struct Empty {};
struct Wrapped {
int* ptr;
[[no_unique_address]] Empty e;
}; // sizeof(Wrapped) can be equal to sizeof(int*)
template<typename K, typename V, typename Hash, typename Equal>
class HashMap {
/* ... */
[[no_unique_address]] Hash hash;
[[no_unique_address]] Equal equal;
};
Exceptions
This rule does not apply to fields whose class has a non-default alignment.